Namespace Core
#Region "Description"
  ' Buffer description:
  ' 00: RESERVED FOR THE SYSTEM
  ' 01: Application Code
  ' 02: Version Number
  ' 03: Customer Id (HI)
  ' 04: Customer Id (LO)
  ' 05: Distributor Id
  ' 06: License Expiration Date : Day (DD)
  ' 07:                           Month (MM)
  ' 08:                           Year (YY) -> YY = Year - 2000
  ' 09: Support Expiration Date : Date (DD)
  ' 10:                           Month (MM)
  ' 11:                           Year (YY) -> YY = Year - 2000
  ' 12: RESERVED FOR THE APPLICATION: These 3 bytes can be freely used by the 
  ' 13:                               applications. See Remark here below to
  ' 14:                               get information on how MORE use them.
  ' 15: RESERVED FOR THE SYSTEM
#End Region

  Public Class LicenseBase
    Private mBuffer() As Byte
    Private mApplicationCode As Byte
    Private mLicenseKey As String

#Region "My extension"
    Private Shared defaultInstance As LicenseBase

    Public Shared ReadOnly Property [Default]() As LicenseBase
      Get
        Return defaultInstance
      End Get
    End Property
#End Region

#Region "Constructors"
#If DEBUG Then
    Public Sub New()
#Else
    Private Sub New()
#End If
      mBuffer = Guid.NewGuid.ToByteArray()
    End Sub

#If DEBUG Then
    Public Sub New(ByVal applicationCode As Byte, ByVal licenseKey As String)
#Else
    Friend Sub New(ByVal applicationCode As Byte, ByVal licenseKey As String)
#End If
      mLicenseKey = licenseKey
      mBuffer = New Guid(licenseKey).ToByteArray()
      mApplicationCode = applicationCode
      Decode()
      If (LicenseExpirationDate > Now) AndAlso (My.Application.Info.Version.Major <= ApplicationVersion) Then defaultInstance = Me
    End Sub

#If DEBUG Then
    Public Sub New(ByVal applicationKey As Byte, _
                   ByVal applicationVersion As Byte, _
                   ByVal customerKey As Integer, _
                   ByVal distributorKey As Byte, _
                   ByVal licenseExpirationDate As Date, _
                   ByVal supportExpirationDate As Date, _
                   ByVal applicationData As Integer)
#Else
    Private Sub New(ByVal applicationKey As Byte, _
                   ByVal applicationVersion As Byte, _
                   ByVal customerKey As Integer, _
                   ByVal distributorKey As Byte, _
                   ByVal licenseExpirationDate As Date, _
                   ByVal supportExpirationDate As Date, _
                   ByVal applicationData As Integer)
#End If
      MyBase.New()
      mBuffer = Guid.NewGuid().ToByteArray()
      Me.ApplicationCode = applicationKey
      Me.ApplicationVersion = applicationVersion
      Me.CustomerCode = customerKey
      Me.DistributorCode = distributorKey
      Me.LicenseExpirationDate = licenseExpirationDate
      Me.SupportExpirationDate = supportExpirationDate
      Me.ApplicationData = applicationData
    End Sub
#End Region

#Region "Properties"
    Protected Friend ReadOnly Property LicenseKey() As String
      Get
        Return mLicenseKey
      End Get
    End Property

    Property ApplicationCode() As Byte
      Get
        Return mBuffer(1)
      End Get
      Set(ByVal value As Byte)
        mBuffer(1) = value
      End Set
    End Property

    Property ApplicationVersion() As Byte
      Get
        Return mBuffer(2)
      End Get
      Set(ByVal value As Byte)
        mBuffer(2) = value
      End Set
    End Property

    Property CustomerCode() As Integer
      Get
        Return mBuffer(3) * 256 + mBuffer(4)
      End Get
      Set(ByVal value As Integer)
        mBuffer(3) = (value And &HFF00) / 256
        mBuffer(4) = (value And &HFF)
      End Set
    End Property

    Property DistributorCode() As Byte
      Get
        Return mBuffer(5)
      End Get
      Set(ByVal value As Byte)
        mBuffer(5) = value
      End Set
    End Property

    Property LicenseExpirationDate() As Date
      Get
        Return New Date(2000 + mBuffer(8), mBuffer(7), mBuffer(6))
      End Get
      Set(ByVal value As Date)
        mBuffer(6) = value.Day
        mBuffer(7) = value.Month
        mBuffer(8) = value.Year - 2000
      End Set
    End Property

    Property SupportExpirationDate() As Date
      Get
        Return New Date(2000 + mBuffer(11), mBuffer(10), mBuffer(9))
      End Get
      Set(ByVal value As Date)
        mBuffer(9) = value.Day
        mBuffer(10) = value.Month
        mBuffer(11) = value.Year - 2000
      End Set
    End Property

    Property ApplicationData() As Integer
      Get
        Return mBuffer(12) * 65536 + mBuffer(13) * 256 + mBuffer(14)
      End Get
      Set(ByVal value As Integer)
        mBuffer(12) = (value And &HFF0000) / 65536
        mBuffer(13) = (value And &HFF00) / 256
        mBuffer(14) = (value And &HFF)
      End Set
    End Property
#End Region

#Region "Methods"
    Private Sub Decode()
      Dim vector As Integer
      Dim idx As Integer
      Dim checkSum As Integer

      vector = mBuffer(15)

      For idx = 14 To 1 Step -1
        mBuffer(15) = mBuffer(idx) Xor vector
        mBuffer(idx) = mBuffer(15) Xor mBuffer(idx)
        vector = mBuffer(15)
      Next
      mBuffer(15) = mBuffer(15) Xor mBuffer(0)
      mBuffer(0) = vector

      For idx = 0 To 15
        checkSum += mBuffer(idx)
      Next

      If ((mBuffer(1) Xor mBuffer(15)) <> mBuffer(0)) Then
        Throw New ArgumentException("Invalid license key")
      ElseIf ApplicationCode <> mApplicationCode AndAlso checkSum <> 0 Then
        Throw New ArgumentException("The license is referring another product")
      ElseIf LicenseExpirationDate < Now.Date Then
        Throw New ArgumentException("The license has expired")
      End If
    End Sub

    'Friend ReadOnly Property License() As String
    '  Get
    '    Return Encode.ToString().ToUpper()
    '  End Get
    'End Property

#If DEBUG Then
    Public Overrides Function ToString() As String
      Return Encode.ToString().ToUpper()
    End Function
#End If

    'Public Function ToGuid() As Guid
    '  Return Encode()
    'End Function

#If DEBUG Then
    Private Function Encode() As Guid
      Dim vector As Byte
      Dim idx As Integer
      Dim bytes() As Byte = mBuffer.Clone()

      mBuffer(15) = Int(Rnd() * 256)

      ' Checksum & Initial vector value
      bytes(0) = bytes(15) Xor bytes(1)

      vector = bytes(15)
      For idx = 0 To 14
        bytes(idx) = bytes(idx) Xor vector
        bytes(15) = bytes(15) Xor bytes(idx)
        vector = bytes(15)
      Next
      bytes(15) = vector

      Return New Guid(bytes)
    End Function
#End If
#End Region
  End Class
End Namespace